Skip to content

allow mGCA const arguments to fall back to anon consts#158617

Merged
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
khyperia:gca-syntax-flip
Jul 10, 2026
Merged

allow mGCA const arguments to fall back to anon consts#158617
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
khyperia:gca-syntax-flip

Conversation

@khyperia

@khyperia khyperia commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

View all comments

makes good progress on rust-lang/project-const-generics#108

min_generic_const_args (mGCA) is all about "is this const lowered to a const item with a body, or, is it "directly" represented and visible in the type system?"

Here's various answers to the question of "can we represent this const arg directly?" - if we answer "no", then we fall back to an anon const (or error, sometimes, depending on context)

on stable, the full list of things can be directly represented is (... it's just the one thing)

  • paths to generic const parameters

on main, under mGCA, before this PR:

  • absolutely everything, if something cannot be represented directly, compiler error
  • const { } gives you an escape hatch to go back to being an anon const

on macroful gca, the directly represented things will be:

  • paths to const parameters
  • direct_const_arg! macro

on macroless gca, the directly represented things will be:

  • paths to const parameters
  • direct_const_arg! macro (not particularly useful under macroless gca)
  • struct expressions, arrays, blah blah
  • currently, paths to anything, even if that produces a compiler error later down the line (potential followup work here...)

this PR implements macroless gca, with the intent of a quick follow-up introducing a "macroless gca" feature and transitioning #[feature(min_generic_const_args)] to be macroful gca

Also of note is that this PR removes the logic to skip generating "fake" DefIds for directly represented AnonConsts under mGCA, we now always generate a DefId when encountering an AnonConst. This lines up with stable, which has a fake AnonConst DefId for a directly represented plain path generic parameter (i.e. the bullet point under the "stable" entry above).

r? @BoxyUwU

@rustbot

rustbot commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

The parser was modified, potentially altering the grammar of (stable) Rust
which would be a breaking change.

cc @fmease

rustfmt is developed in its own repository. If possible, consider making this change to rust-lang/rustfmt instead.

cc @rust-lang/rustfmt

clippy is developed in its own repository. If possible, consider making this change to rust-lang/rust-clippy instead.

cc @rust-lang/clippy

Some changes occurred in compiler/rustc_builtin_macros/src/autodiff.rs

cc @ZuseZ4

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue. labels Jun 30, 2026
@rustbot

rustbot commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

BoxyUwU is currently at their maximum review capacity.
They may take a while to respond.

@ytmimi ytmimi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great to also add a simple rustfmt test case annotated with #![feature(min_generic_const_args)] so we know it's for an unstable feature.

View changes since this review

Comment thread src/tools/rustfmt/src/types.rs Outdated
Comment on lines +1057 to +1060
ast::TyKind::DirectConstArg(ref expr) => {
let expr = expr.rewrite_result(context, shape)?;
Ok(format!("core::direct_const_arg!({expr})"))
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just double checking that core::direct_const_arg! won't show up as a macro call in the AST. Might be better to just return Err(RewriteError::Unknown) or even Ok(context.snippet(self.span).to_owned()) to return the span unchanged.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, good point, I didn't quite realize how rustfmt works, thank you! could you please double-check what I just force-pushed to make sure it's what you were thinking of?

in particular, I'm not totally sure why ast::ExprKind::Err(_) | ast::ExprKind::Dummy return Err(RewriteError::Unknown), but ast::TyKind::Dummy | ast::TyKind::Err(_) return Ok(context.snippet(self.span).to_owned()). I kept doing that for DirectConstArg, but, yeah.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would expect this codepath to actually be unreachable. direct_const_arg! is a macro call in the AST and rustfmt will see the unexpanded AST so we'll never encounter a DirectConstArg expr/ty 🤔 does ICEing here cause any issues?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh this is the:

rustfmt tries to parse macro arguments when formatting macros, so it's not
totally impossible for rustfmt to come across one of these nodes when formatting
a file

thats funny

@BoxyUwU BoxyUwU left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rhs: Some(self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?),
},
(true, true) => {
ConstItemRhsKind::TypeConst { rhs: Some(self.parse_expr_anon_const()?) }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should track somewhere to stop parsing type const rhs' differently than normal const items I think :3 this goes hand in hand with i guess the stuff about lowering the rhs of type consts as if they were direct'd?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

precisely and exactly. very much on my todo list for the followup I was talking about!

self.with_parent(parent, |this| visit::walk_anon_const(this, constant));
}
};
let parent =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hurray less def collector and parsing jank 😌

Comment thread compiler/rustc_ast_lowering/src/lib.rs Outdated
/// it cannot.
#[instrument(level = "debug", skip(self), ret)]
fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir> {
fn try_lower_expr_to_const_arg_direct(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fn is like slightly scuffed but I need to think a bit about why that is and what an alternative might be :3

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's SO scuffed, and it's gonna get worse once we introduce macroful gca.

the main issue IMO is that we need a separate "check" and "actually do" phase, because we could fail several layers deep - e.g. we're inside a tuple, ((lowering, stuff), (and + then + we + error)), if we have already lowered and allocated arena memory when lowering (lowering, stuff), we cannot then bail out when we encounter the addition expr, because then all those generated IDs and arena memory and stuff would get unused.

Comment thread compiler/rustc_ast_lowering/src/lib.rs Outdated
ExprKind::Tup(exprs) if is_mgca => {
if check_only {
for expr in exprs {
let _ = self.try_lower_expr_to_const_arg_direct(expr, None, check_only)?;

@BoxyUwU BoxyUwU Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iirc the reason we recurse into tuple elements but not path arguments is that we can't support (N, 1 + 1) without (N, const { 1 + 1 }) because we dont want to make a defid for all tuple element exprs in advance so that we can reuse the defid here

should write that down somewhere in here as the inconsistency between paths and other exprs feels slightly weird :3

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. if we generated defids for everything, we wouldn't need this weird separate "check" and "actually do" phases, because we could always bail to representing things as anon consts if we fail to lower some later tuple element after successfully lowering previous tuple elements, and (N, 1 + 1) would sorta implicitly have a const { 1 + 1 } block.

... but generating defids for everything is, Not Great

},
_ => false,
}
&& matches!(tcx.hir_node_by_def_id(local_id), hir::Node::ConstArg(_))

@BoxyUwU BoxyUwU Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would somewhat not surprise me if this causes ICEs, I would expect that if we have these defids in the parent tree then we actually do need to encode them 🤔

View changes since the review

#![feature(min_generic_const_args)]
#![allow(incomplete_features)]
pub struct S<const N: usize>;
pub fn f() -> S<{ const { 1 + 1 } }> {

@BoxyUwU BoxyUwU Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this an anon const with a const block inside it? can we have comment about what we expect this to lower to :3

View changes since the review

@BoxyUwU BoxyUwU left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we get a test that using direct_const_arg without any feature enabled gives a feature gate error even when using it in const arg position, e.g. fn foo<const N: usize>() -> [(); direct_const_arg!(N)] should error

View changes since this review

Comment on lines +11 to +17
let mut parser = cx.new_parser_from_tts(tts);
let expr = match parser.parse_expr() {
Ok(parsed) => parsed,
Err(err) => {
return ExpandResult::Ready(DummyResult::any(span, err.emit()));
}
};

@Shourya742 Shourya742 Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have this question, like do we expect the direct_const_arg! to contain multiple expressions?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point! fixed now :3

@khyperia khyperia force-pushed the gca-syntax-flip branch 2 times, most recently from eb61cdf to 44acd42 Compare July 1, 2026 15:31
@rust-bors

This comment has been minimized.

@rustbot

This comment has been minimized.

@BoxyUwU

BoxyUwU commented Jul 6, 2026

Copy link
Copy Markdown
Member

r=me if CI passes

@khyperia

khyperia commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@bors r=BoxyUwU

@rust-bors

rust-bors Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

📌 Commit 7f39e9f has been approved by BoxyUwU

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 7, 2026
m-ou-se added a commit to m-ou-se/rust that referenced this pull request Jul 7, 2026
allow mGCA const arguments to fall back to anon consts

makes good progress on rust-lang/project-const-generics#108

`min_generic_const_args` (mGCA) is all about "is this const lowered to a const item with a body, or, is it "directly" represented and visible in the type system?"

Here's various answers to the question of "can we represent this const arg directly?" - if we answer "no", then we fall back to an anon const (or error, sometimes, depending on context)

on **stable**, the full list of things can be directly represented is (... it's just the one thing)

- paths to generic const parameters

on `main`, under mGCA, **before** this PR:

- absolutely everything, if something cannot be represented directly, compiler error
- `const { }` gives you an escape hatch to go back to being an anon const

on macro**ful** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro

on macro**less** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro (not *particularly* useful under macro**less** gca)
- struct expressions, arrays, blah blah
- currently, paths to *anything*, even if that produces a compiler error later down the line (potential followup work here...)

this PR implements macro**less** gca, with the intent of a quick follow-up introducing a "macroless gca" feature and transitioning `#[feature(min_generic_const_args)]` to be macro**ful** gca

Also of note is that this PR removes the logic to skip generating "fake" DefIds for directly represented AnonConsts under mGCA, we now always generate a DefId when encountering an AnonConst. This lines up with stable, which has a fake AnonConst DefId for a directly represented plain path generic parameter (i.e. the bullet point under the "stable" entry above).

r? @BoxyUwU
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jul 9, 2026
allow mGCA const arguments to fall back to anon consts

makes good progress on rust-lang/project-const-generics#108

`min_generic_const_args` (mGCA) is all about "is this const lowered to a const item with a body, or, is it "directly" represented and visible in the type system?"

Here's various answers to the question of "can we represent this const arg directly?" - if we answer "no", then we fall back to an anon const (or error, sometimes, depending on context)

on **stable**, the full list of things can be directly represented is (... it's just the one thing)

- paths to generic const parameters

on `main`, under mGCA, **before** this PR:

- absolutely everything, if something cannot be represented directly, compiler error
- `const { }` gives you an escape hatch to go back to being an anon const

on macro**ful** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro

on macro**less** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro (not *particularly* useful under macro**less** gca)
- struct expressions, arrays, blah blah
- currently, paths to *anything*, even if that produces a compiler error later down the line (potential followup work here...)

this PR implements macro**less** gca, with the intent of a quick follow-up introducing a "macroless gca" feature and transitioning `#[feature(min_generic_const_args)]` to be macro**ful** gca

Also of note is that this PR removes the logic to skip generating "fake" DefIds for directly represented AnonConsts under mGCA, we now always generate a DefId when encountering an AnonConst. This lines up with stable, which has a fake AnonConst DefId for a directly represented plain path generic parameter (i.e. the bullet point under the "stable" entry above).

r? @BoxyUwU
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jul 9, 2026
allow mGCA const arguments to fall back to anon consts

makes good progress on rust-lang/project-const-generics#108

`min_generic_const_args` (mGCA) is all about "is this const lowered to a const item with a body, or, is it "directly" represented and visible in the type system?"

Here's various answers to the question of "can we represent this const arg directly?" - if we answer "no", then we fall back to an anon const (or error, sometimes, depending on context)

on **stable**, the full list of things can be directly represented is (... it's just the one thing)

- paths to generic const parameters

on `main`, under mGCA, **before** this PR:

- absolutely everything, if something cannot be represented directly, compiler error
- `const { }` gives you an escape hatch to go back to being an anon const

on macro**ful** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro

on macro**less** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro (not *particularly* useful under macro**less** gca)
- struct expressions, arrays, blah blah
- currently, paths to *anything*, even if that produces a compiler error later down the line (potential followup work here...)

this PR implements macro**less** gca, with the intent of a quick follow-up introducing a "macroless gca" feature and transitioning `#[feature(min_generic_const_args)]` to be macro**ful** gca

Also of note is that this PR removes the logic to skip generating "fake" DefIds for directly represented AnonConsts under mGCA, we now always generate a DefId when encountering an AnonConst. This lines up with stable, which has a fake AnonConst DefId for a directly represented plain path generic parameter (i.e. the bullet point under the "stable" entry above).

r? @BoxyUwU
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jul 9, 2026
allow mGCA const arguments to fall back to anon consts

makes good progress on rust-lang/project-const-generics#108

`min_generic_const_args` (mGCA) is all about "is this const lowered to a const item with a body, or, is it "directly" represented and visible in the type system?"

Here's various answers to the question of "can we represent this const arg directly?" - if we answer "no", then we fall back to an anon const (or error, sometimes, depending on context)

on **stable**, the full list of things can be directly represented is (... it's just the one thing)

- paths to generic const parameters

on `main`, under mGCA, **before** this PR:

- absolutely everything, if something cannot be represented directly, compiler error
- `const { }` gives you an escape hatch to go back to being an anon const

on macro**ful** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro

on macro**less** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro (not *particularly* useful under macro**less** gca)
- struct expressions, arrays, blah blah
- currently, paths to *anything*, even if that produces a compiler error later down the line (potential followup work here...)

this PR implements macro**less** gca, with the intent of a quick follow-up introducing a "macroless gca" feature and transitioning `#[feature(min_generic_const_args)]` to be macro**ful** gca

Also of note is that this PR removes the logic to skip generating "fake" DefIds for directly represented AnonConsts under mGCA, we now always generate a DefId when encountering an AnonConst. This lines up with stable, which has a fake AnonConst DefId for a directly represented plain path generic parameter (i.e. the bullet point under the "stable" entry above).

r? @BoxyUwU
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `_`
|
= note: cannot satisfy `<A as Array>::arr::{constant#0} == _`
= note: cannot satisfy `<A as Array>::arr::{constant#0}::{constant#0} == _`

@Shourya742 Shourya742 Jul 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious on why this is duplicated? I will try to investigate more on this.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is intentional! we now generate potentially "fake" DefIds for anon consts (see diff to compiler/rustc_resolve/src/def_collector.rs).

  • if the anon const that the DefId was created for is actually used in compiler/rustc_ast_lowering/src/lib.rs, yippee, nice, use the DefId for the anon const.
  • if it isn't, shoot, we need to shove the DefId somewhere, it's required to be used in the HIR output, uuuh, shove it on the ConstArg I guess (this is a "fake" DefId)

notably, the def_collector runs before we're able to know whether we're able to represent the syntax directly or not, so it cannot know whether to generate a DefId ahead of time or not.

this matches the behavior of stable, for min_const_generic simple paths to generic parameters (min_const_generic_args experimented with yeeting this fake DefId, but that turned out to not be viable in the long run, so this PR adds 'em back)

@khyperia khyperia Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in other words, for the syntax [u8; const { Self::LEN }] in the test file, there are now two constants:

  • the automatically generated anon const ID (this is the new one). It is placed on a random ConstArg. Confusingly, this is a ConstArgKind::Anon(..), which holds...
  • ... the inner const{} block, which has a DefId that is a DefKind::InlineConst

("confusingly" because you could also consider, say, [u8; (2, const { 5 })] - ignoring the nonsense type, the fake anon const ID would be placed on the ConstArgKind::Tuple. But because it's directly representing... another anon const... it's on ConstArgKind::Anon(..). See also the test I added tests/ui/const-generics/mgca/serialized-direct-const-with-anon-const-body.rs which has another comment describing this situation, an anon const directly inside of another anon const)

... I feel like I'm just being confusing now, sorry. Feel free to DM me on zulip if you're confused, haha

/// Creates a new style directly represented const argument.
/// ```ignore (cannot test this from within core yet)
/// type const BAR<const N: usize>: usize = N;
/// type const FOO<const N: usize>: usize = direct!(BAR::<N>);

@Shourya742 Shourya742 Jul 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
/// type const FOO<const N: usize>: usize = direct!(BAR::<N>);
/// type const FOO<const N: usize>: usize = direct_const_arg!(BAR::<N>);

View changes since the review

rust-bors Bot pushed a commit that referenced this pull request Jul 9, 2026
…uwer

Rollup of 21 pull requests

Successful merges:

 - #150946 (intrinsics: Add a fallback for non-const libm float functions)
 - #158510 (Enable `static_position_independent_executables` on all gnu and musl targets)
 - #158541 (Move `std::io::Write` to `core::io`)
 - #158899 (Fix `unaligned_volatile_store` by removing `MemFlags::UNALIGNED`)
 - #155429 (Support `u128`/`i128` c-variadic arguments)
 - #156370 (Reject linked dylib EII default overrides)
 - #156508 (Infer all anonymous lifetimes in assoc consts as `'static`)
 - #158617 (allow mGCA const arguments to fall back to anon consts)
 - #158645 (Fix splat ICEs and ban it in closures)
 - #158859 (Improve `-Zls` diagnostic message on `.rs` files)
 - #158988 (Redo `TokenStreamIter`)
 - #158347 (Improve generic parameters handling for #[diagnostic::on_const])
 - #158722 (delegation: do not always inherit `ConstArgHasType` predicates)
 - #158739 (view-types: HIR lowering)
 - #158883 (tests: fix enum-match.rs to handle LLVM 23)
 - #158886 (Add documentation for the `no_std` attribute)
 - #158940 (Implement feature `char_to_u32`)
 - #158951 (Merge three `MaxUniverse`s into one)
 - #158961 (Reapply "LLVM 23: Run AssignGUIDPass in some places")
 - #158996 ([compiler] Implement `PartialOrd` via `Ord` for `Span` and newtype_indexes)
 - #159005 (Use `as_lang_item` instead of repeatedly matching)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jul 9, 2026
allow mGCA const arguments to fall back to anon consts

makes good progress on rust-lang/project-const-generics#108

`min_generic_const_args` (mGCA) is all about "is this const lowered to a const item with a body, or, is it "directly" represented and visible in the type system?"

Here's various answers to the question of "can we represent this const arg directly?" - if we answer "no", then we fall back to an anon const (or error, sometimes, depending on context)

on **stable**, the full list of things can be directly represented is (... it's just the one thing)

- paths to generic const parameters

on `main`, under mGCA, **before** this PR:

- absolutely everything, if something cannot be represented directly, compiler error
- `const { }` gives you an escape hatch to go back to being an anon const

on macro**ful** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro

on macro**less** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro (not *particularly* useful under macro**less** gca)
- struct expressions, arrays, blah blah
- currently, paths to *anything*, even if that produces a compiler error later down the line (potential followup work here...)

this PR implements macro**less** gca, with the intent of a quick follow-up introducing a "macroless gca" feature and transitioning `#[feature(min_generic_const_args)]` to be macro**ful** gca

Also of note is that this PR removes the logic to skip generating "fake" DefIds for directly represented AnonConsts under mGCA, we now always generate a DefId when encountering an AnonConst. This lines up with stable, which has a fake AnonConst DefId for a directly represented plain path generic parameter (i.e. the bullet point under the "stable" entry above).

r? @BoxyUwU
rust-bors Bot pushed a commit that referenced this pull request Jul 9, 2026
…uwer

Rollup of 24 pull requests

Successful merges:

 - #150946 (intrinsics: Add a fallback for non-const libm float functions)
 - #158510 (Enable `static_position_independent_executables` on all gnu and musl targets)
 - #158541 (Move `std::io::Write` to `core::io`)
 - #158899 (Fix `unaligned_volatile_store` by removing `MemFlags::UNALIGNED`)
 - #156027 (Consider captured regions for opaque type region liveness.)
 - #156370 (Reject linked dylib EII default overrides)
 - #156508 (Infer all anonymous lifetimes in assoc consts as `'static`)
 - #157561 (rustdoc: do not include extra stuff in span)
 - #158617 (allow mGCA const arguments to fall back to anon consts)
 - #158645 (Fix splat ICEs and ban it in closures)
 - #158859 (Improve `-Zls` diagnostic message on `.rs` files)
 - #158988 (Redo `TokenStreamIter`)
 - #158347 (Improve generic parameters handling for #[diagnostic::on_const])
 - #158384 (Allow BackwardIncompatibleDropHint in polonius legacy)
 - #158722 (delegation: do not always inherit `ConstArgHasType` predicates)
 - #158739 (view-types: HIR lowering)
 - #158877 (borrowck: Keep returned `path` from `best_blame_constraint()` consistent)
 - #158883 (tests: fix enum-match.rs to handle LLVM 23)
 - #158886 (Add documentation for the `no_std` attribute)
 - #158940 (Implement feature `char_to_u32`)
 - #158951 (Merge three `MaxUniverse`s into one)
 - #158961 (Reapply "LLVM 23: Run AssignGUIDPass in some places")
 - #158995 (Use REST API in linkchecker script)
 - #158996 ([compiler] Implement `PartialOrd` via `Ord` for `Span` and newtype_indexes)
jhpratt added a commit to jhpratt/rust that referenced this pull request Jul 9, 2026
allow mGCA const arguments to fall back to anon consts

makes good progress on rust-lang/project-const-generics#108

`min_generic_const_args` (mGCA) is all about "is this const lowered to a const item with a body, or, is it "directly" represented and visible in the type system?"

Here's various answers to the question of "can we represent this const arg directly?" - if we answer "no", then we fall back to an anon const (or error, sometimes, depending on context)

on **stable**, the full list of things can be directly represented is (... it's just the one thing)

- paths to generic const parameters

on `main`, under mGCA, **before** this PR:

- absolutely everything, if something cannot be represented directly, compiler error
- `const { }` gives you an escape hatch to go back to being an anon const

on macro**ful** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro

on macro**less** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro (not *particularly* useful under macro**less** gca)
- struct expressions, arrays, blah blah
- currently, paths to *anything*, even if that produces a compiler error later down the line (potential followup work here...)

this PR implements macro**less** gca, with the intent of a quick follow-up introducing a "macroless gca" feature and transitioning `#[feature(min_generic_const_args)]` to be macro**ful** gca

Also of note is that this PR removes the logic to skip generating "fake" DefIds for directly represented AnonConsts under mGCA, we now always generate a DefId when encountering an AnonConst. This lines up with stable, which has a fake AnonConst DefId for a directly represented plain path generic parameter (i.e. the bullet point under the "stable" entry above).

r? @BoxyUwU
rust-bors Bot pushed a commit that referenced this pull request Jul 10, 2026
Rollup of 24 pull requests

Successful merges:

 - #150946 (intrinsics: Add a fallback for non-const libm float functions)
 - #158541 (Move `std::io::Write` to `core::io`)
 - #156027 (Consider captured regions for opaque type region liveness.)
 - #156370 (Reject linked dylib EII default overrides)
 - #156508 (Infer all anonymous lifetimes in assoc consts as `'static`)
 - #157561 (rustdoc: do not include extra stuff in span)
 - #158617 (allow mGCA const arguments to fall back to anon consts)
 - #158645 (Fix splat ICEs and ban it in closures)
 - #158859 (Improve `-Zls` diagnostic message on `.rs` files)
 - #158988 (Redo `TokenStreamIter`)
 - #158347 (Improve generic parameters handling for #[diagnostic::on_const])
 - #158384 (Allow BackwardIncompatibleDropHint in polonius legacy)
 - #158722 (delegation: do not always inherit `ConstArgHasType` predicates)
 - #158739 (view-types: HIR lowering)
 - #158877 (borrowck: Keep returned `path` from `best_blame_constraint()` consistent)
 - #158883 (tests: fix enum-match.rs to handle LLVM 23)
 - #158886 (Add documentation for the `no_std` attribute)
 - #158940 (Implement feature `char_to_u32`)
 - #158951 (Merge three `MaxUniverse`s into one)
 - #158960 (Fix bootstrap submodule path prefix matching)
 - #158961 (Reapply "LLVM 23: Run AssignGUIDPass in some places")
 - #158995 (Use REST API in linkchecker script)
 - #158996 ([compiler] Implement `PartialOrd` via `Ord` for `Span` and newtype_indexes)
 - #159036 (bootstrap: expand '@argfile' arguments to rustc shim)
@rust-bors rust-bors Bot merged commit 3dc22a3 into rust-lang:main Jul 10, 2026
13 checks passed
@rustbot rustbot added this to the 1.99.0 milestone Jul 10, 2026
rust-timer added a commit that referenced this pull request Jul 10, 2026
Rollup merge of #158617 - khyperia:gca-syntax-flip, r=BoxyUwU

allow mGCA const arguments to fall back to anon consts

makes good progress on rust-lang/project-const-generics#108

`min_generic_const_args` (mGCA) is all about "is this const lowered to a const item with a body, or, is it "directly" represented and visible in the type system?"

Here's various answers to the question of "can we represent this const arg directly?" - if we answer "no", then we fall back to an anon const (or error, sometimes, depending on context)

on **stable**, the full list of things can be directly represented is (... it's just the one thing)

- paths to generic const parameters

on `main`, under mGCA, **before** this PR:

- absolutely everything, if something cannot be represented directly, compiler error
- `const { }` gives you an escape hatch to go back to being an anon const

on macro**ful** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro

on macro**less** gca, the directly represented things will be:

- paths to const parameters
- `direct_const_arg!` macro (not *particularly* useful under macro**less** gca)
- struct expressions, arrays, blah blah
- currently, paths to *anything*, even if that produces a compiler error later down the line (potential followup work here...)

this PR implements macro**less** gca, with the intent of a quick follow-up introducing a "macroless gca" feature and transitioning `#[feature(min_generic_const_args)]` to be macro**ful** gca

Also of note is that this PR removes the logic to skip generating "fake" DefIds for directly represented AnonConsts under mGCA, we now always generate a DefId when encountering an AnonConst. This lines up with stable, which has a fake AnonConst DefId for a directly represented plain path generic parameter (i.e. the bullet point under the "stable" entry above).

r? @BoxyUwU
@Shourya742

Copy link
Copy Markdown
Member

I was thinking about this while reviewing the PR, and wanted to double-check one resolver path.

For S<{ N }>, the compiler knows from the syntax that this is a const argument. It is represented roughly as GenericArg::Const(AnonConst { .. }), so it naturally goes through the const-arg resolver path.

For S<N>, the compiler cannot know from parsing alone whether N is a type argument or a const argument. So it is initially represented more like GenericArg::Type(TyKind::Path(...)). Later, in visit_generic_arg, we have a special logic to check whether this path-like type argument is actually a trivial const argument. If it is, we resolves it through the const-arg path; otherwise it continues with normal visit_ty.

For S<direct_const_arg!(N)>, I think it also starts out as a type-looking generic argument, probably as a macro call in type position. After macro expansion, it becomes something like GenericArg::Type(TyKind::DirectConstArg(expr)). I wonder if we should handle TyKind::DirectConstArg in visit_generic_arg, next to the existing TyKind::Path disambiguation logic, and resolve its inner expression with resolve_anon_const_manual. Otherwise it seems like it may fall through to normal visit_ty.

Curious to know what you think about this?

@khyperia khyperia deleted the gca-syntax-flip branch July 10, 2026 05:32
@khyperia

Copy link
Copy Markdown
Contributor Author

true! to be honest, I haven't thought that much about the resolver, but yeah, right now, TyKind::DirectConstArg is currently nameres'd as just a plain visit::walk_ty(self, ty), no additional ribs. So we probably accept some things that we shouldn't, or resolve lifetimes wonkily due to the lack of a LifetimeRibKind::elided(LifetimeRes::Infer) that is typically on anon consts.

I might address this at the same time as addressing this #158617 (comment) (make the rhs of type consts less special), that involves mucking around with nameres a decent amount and it might be easiest to deal with both cases at the same time.

@JonathanBrouwer

Copy link
Copy Markdown
Contributor

@rust-timer build 34f29a2

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (34f29a2): comparison URL.

Overall result: ❌✅ regressions and improvements - please read:

Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf.

Next, please: If you can, justify the regressions found in this try perf run in writing along with @rustbot label: +perf-regression-triaged. If not, fix the regressions and do another perf run. Neutral or positive results will clear the label automatically.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.2% [0.2%, 0.2%] 1
Regressions ❌
(secondary)
0.1% [0.0%, 0.2%] 7
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.0% [-0.1%, -0.0%] 3
All ❌✅ (primary) 0.2% [0.2%, 0.2%] 1

Max RSS (memory usage)

Results (primary -3.5%, secondary -1.7%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
1.5% [1.5%, 1.5%] 1
Improvements ✅
(primary)
-3.5% [-3.5%, -3.5%] 1
Improvements ✅
(secondary)
-2.8% [-3.7%, -2.0%] 3
All ❌✅ (primary) -3.5% [-3.5%, -3.5%] 1

Cycles

Results (primary 2.9%, secondary 0.0%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
2.9% [2.9%, 2.9%] 1
Regressions ❌
(secondary)
4.7% [2.2%, 12.8%] 6
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-3.5% [-8.9%, -1.6%] 8
All ❌✅ (primary) 2.9% [2.9%, 2.9%] 1

Binary size

This perf run didn't have relevant results for this metric.

Bootstrap: 490.768s -> 489.602s (-0.24%)
Artifact size: 389.74 MiB -> 389.17 MiB (-0.15%)

@rustbot rustbot added the perf-regression Performance regression. label Jul 10, 2026
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jul 10, 2026
merge DefKind::InlineConst into AnonConst

This is a closely related followup to rust-lang#158375 (a condition of merging that PR was doing this as a followup)

This merge conflicts with rust-lang#158617 ; prefer merging that one first please~

This PR is within the general goal of const generics / `generic_const_args` and perhaps specifically rust-lang/project-const-generics#108 but is mostly a code cleanup rather than implementing any particular feature

---

Anon consts (the `1 + 2` in `let x: [u8; 1 + 2];`) are closely related to inline consts (the `const {}` in `let x = const { 1 + 2 };`). They are especially related now that both can be used in the type system (by the above PR) and should be treated identically in that scenario. In general, they should be treated the same, as evidenced by the number of matches touched in this PR that just match on both. This PR merges the two DefKinds into one, mostly arbitrarily and bikesheddily choosing to use the name `AnonConst` for the merged concept (e.g. both `DefKind::InlineConst` and `DefKind::AnonConst` used `ast::AnonConst`, so `ast::AnonConst` should probably be named whatever the merged `DefKind` concept is named).

When you absolute must distinguish between the two, `tcx.anon_const_kind` allows you to do so - however, anon consts/inline consts in the type system intentionally appear identically under `anon_const_kind`. Indeed, the places where we distinguish between the two raised my eyebrows a few times when doing this PR, and being a little more explicit and loud about it is helpful for code clarity, IMO.

r? @BoxyUwU
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jul 10, 2026
merge DefKind::InlineConst into AnonConst

This is a closely related followup to rust-lang#158375 (a condition of merging that PR was doing this as a followup)

This merge conflicts with rust-lang#158617 ; prefer merging that one first please~

This PR is within the general goal of const generics / `generic_const_args` and perhaps specifically rust-lang/project-const-generics#108 but is mostly a code cleanup rather than implementing any particular feature

---

Anon consts (the `1 + 2` in `let x: [u8; 1 + 2];`) are closely related to inline consts (the `const {}` in `let x = const { 1 + 2 };`). They are especially related now that both can be used in the type system (by the above PR) and should be treated identically in that scenario. In general, they should be treated the same, as evidenced by the number of matches touched in this PR that just match on both. This PR merges the two DefKinds into one, mostly arbitrarily and bikesheddily choosing to use the name `AnonConst` for the merged concept (e.g. both `DefKind::InlineConst` and `DefKind::AnonConst` used `ast::AnonConst`, so `ast::AnonConst` should probably be named whatever the merged `DefKind` concept is named).

When you absolute must distinguish between the two, `tcx.anon_const_kind` allows you to do so - however, anon consts/inline consts in the type system intentionally appear identically under `anon_const_kind`. Indeed, the places where we distinguish between the two raised my eyebrows a few times when doing this PR, and being a little more explicit and loud about it is helpful for code clarity, IMO.

r? @BoxyUwU
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jul 10, 2026
merge DefKind::InlineConst into AnonConst

This is a closely related followup to rust-lang#158375 (a condition of merging that PR was doing this as a followup)

This merge conflicts with rust-lang#158617 ; prefer merging that one first please~

This PR is within the general goal of const generics / `generic_const_args` and perhaps specifically rust-lang/project-const-generics#108 but is mostly a code cleanup rather than implementing any particular feature

---

Anon consts (the `1 + 2` in `let x: [u8; 1 + 2];`) are closely related to inline consts (the `const {}` in `let x = const { 1 + 2 };`). They are especially related now that both can be used in the type system (by the above PR) and should be treated identically in that scenario. In general, they should be treated the same, as evidenced by the number of matches touched in this PR that just match on both. This PR merges the two DefKinds into one, mostly arbitrarily and bikesheddily choosing to use the name `AnonConst` for the merged concept (e.g. both `DefKind::InlineConst` and `DefKind::AnonConst` used `ast::AnonConst`, so `ast::AnonConst` should probably be named whatever the merged `DefKind` concept is named).

When you absolute must distinguish between the two, `tcx.anon_const_kind` allows you to do so - however, anon consts/inline consts in the type system intentionally appear identically under `anon_const_kind`. Indeed, the places where we distinguish between the two raised my eyebrows a few times when doing this PR, and being a little more explicit and loud about it is helpful for code clarity, IMO.

r? @BoxyUwU
jhpratt added a commit to jhpratt/rust that referenced this pull request Jul 11, 2026
merge DefKind::InlineConst into AnonConst

This is a closely related followup to rust-lang#158375 (a condition of merging that PR was doing this as a followup)

This merge conflicts with rust-lang#158617 ; prefer merging that one first please~

This PR is within the general goal of const generics / `generic_const_args` and perhaps specifically rust-lang/project-const-generics#108 but is mostly a code cleanup rather than implementing any particular feature

---

Anon consts (the `1 + 2` in `let x: [u8; 1 + 2];`) are closely related to inline consts (the `const {}` in `let x = const { 1 + 2 };`). They are especially related now that both can be used in the type system (by the above PR) and should be treated identically in that scenario. In general, they should be treated the same, as evidenced by the number of matches touched in this PR that just match on both. This PR merges the two DefKinds into one, mostly arbitrarily and bikesheddily choosing to use the name `AnonConst` for the merged concept (e.g. both `DefKind::InlineConst` and `DefKind::AnonConst` used `ast::AnonConst`, so `ast::AnonConst` should probably be named whatever the merged `DefKind` concept is named).

When you absolute must distinguish between the two, `tcx.anon_const_kind` allows you to do so - however, anon consts/inline consts in the type system intentionally appear identically under `anon_const_kind`. Indeed, the places where we distinguish between the two raised my eyebrows a few times when doing this PR, and being a little more explicit and loud about it is helpful for code clarity, IMO.

r? @BoxyUwU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

perf-regression Performance regression. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants